from . import CompoundElement

import bs4
import six
"""The purpose of this module is to provide classes corresponding to
most elements (except <style>, <script> and similar non-document
content elements) and core attributes (except style and the %events
attributes) of HTML4.01 and HTML5. It is not totally compliant with
the HTML4.01 and HTML5 standards, but is enough to model most
real-world HTML. It contains no provisions to ensure that elements of
a particular kind only contain allowed sub-elements."""


# abstract class
class HTMLElement(CompoundElement): pass

class HTML(HTMLElement):  pass
class Head(HTMLElement):  pass
# a title cannot contain subelements -- derive from UnicodeElement instead?
class Title(HTMLElement): pass

class Body(HTMLElement):

    def as_xhtml(self, uri):
        element = super(Body, self).as_xhtml(uri)
        element.set('about', uri)
        return element
    
# %block
class P(HTMLElement):     pass
# %heading
class H1(HTMLElement): pass
class H2(HTMLElement): pass
class H3(HTMLElement): pass
class H4(HTMLElement): pass
class H5(HTMLElement): pass
class H6(HTMLElement): pass
# %list
class UL(HTMLElement): pass
class OL(HTMLElement): pass
class LI(HTMLElement): pass
# %preformatted
class Pre(HTMLElement): pass
# other
class DL(HTMLElement): pass
class DT(HTMLElement): pass
class DD(HTMLElement): pass
class Div(HTMLElement): pass
class Blockquote(HTMLElement): pass
class Form(HTMLElement): pass
class HR(HTMLElement): pass
class Table(HTMLElement): pass
class Fieldset(HTMLElement): pass
class Address(HTMLElement): pass
# %fontstyle
class TT (HTMLElement): pass
class I (HTMLElement): pass
class B (HTMLElement): pass
class U (HTMLElement): pass
class Big (HTMLElement): pass
class Small(HTMLElement): pass
# %phrase
class Em (HTMLElement): pass
class Strong (HTMLElement): pass
class Dfn (HTMLElement): pass
class Code (HTMLElement): pass
class Samp (HTMLElement): pass
class Kbd (HTMLElement): pass
class Var (HTMLElement): pass
class Cite (HTMLElement): pass
class Abbr (HTMLElement): pass
class Acronym(HTMLElement): pass
# %special
class A (HTMLElement): pass
class Img (HTMLElement): pass
class Object (HTMLElement): pass
class Br (HTMLElement): pass
class Q (HTMLElement): pass
class Sub (HTMLElement): pass
class Sup (HTMLElement): pass
class Span (HTMLElement): pass
class BDO(HTMLElement): pass
# %form
class Input(HTMLElement): pass
class Select(HTMLElement): pass
class Textarea(HTMLElement): pass
class Label(HTMLElement): pass
class Button(HTMLElement): pass
# table
class Caption(HTMLElement): pass
class Thead(HTMLElement): pass
class Tfoot(HTMLElement): pass
class Tbody(HTMLElement): pass
class Colgroup(HTMLElement): pass
class Col(HTMLElement): pass
class TR(HTMLElement): pass
class TH(HTMLElement): pass
class TD(HTMLElement): pass

# very special?
class Ins(HTMLElement):   pass
class Del(HTMLElement):   pass

# new in HTML5 -- cannot be simply expressed in XHTML 1.1. Instead
# they're expressed as eg. '<div class="section">'
class HTML5Element(HTMLElement):
    tagname = "div"
    def _get_classname(self):
        return self.__class__.__name__.lower()
    classname = property(_get_classname)

class Article(HTML5Element): pass
class Aside(HTML5Element): pass
class Bdi(HTML5Element): pass
class Details(HTML5Element): pass
class Dialog(HTML5Element): pass
class Summary(HTML5Element): pass
class Figure(HTML5Element): pass
class Figcaption(HTML5Element): pass
class Footer(HTML5Element): pass
class Header(HTML5Element): pass
class Hgroup(HTML5Element): pass
class Mark(HTML5Element): pass
class Meter(HTML5Element): pass
class Nav(HTML5Element): pass
class Progress(HTML5Element): pass
class Ruby(HTML5Element): pass
class Rt(HTML5Element): pass
class Rp(HTML5Element): pass
class Section(HTML5Element): pass
class Time(HTML5Element): pass
class Wbr(HTML5Element): pass
# audio, video, embed, canvas and similar non structural/semantic
# elements not inclided

# For use by elements_from_soup. FIXME: we should be able to build
# _tagmap dynamically.
_tagmap = {'html': HTML,
           'head': Head,
           'title': Title,
           'body': Body,
           'p': P,
           'h1': H1,
           'h2': H2,
           'h3': H3,
           'h4': H4,
           'h5': H5,
           'h6': H6,
           'ul': UL,
           'ol': OL,
           'li': LI,
           'pre': Pre,
           'dl': DL,
           'dt': DT,
           'dd': DD,
           'div': Div,
           'blockquote': Blockquote,
           'form': Form,
           'hr': HR,
           'table': Table,
           'fieldset': Fieldset,
           'address': Address,
           'tt': TT,
           'i': I,
           'b': B,
           'u': U,
           'big': Big,
           'small': Small,
           'em': Em,
           'strong': Strong,
           'dfn': Dfn,
           'code': Code,
           'samp': Samp,
           'kbd': Kbd,
           'var': Var,
           'cite': Cite,
           'abbr': Abbr,
           'acronym': Acronym,
           'a': A,
           'img': Img,
           'object': Object,
           'br': Br,
           'q': Q,
           'sub': Sub,
           'sup': Sup,
           'span': Span,
           'bdo': BDO,
           'input': Input,
           'select': Select,
           'textarea': Textarea,
           'label': Label,
           'button': Button,
           'caption': Caption,
           'thead': Thead,
           'tfoot': Tfoot,
           'tbody': Tbody,
           'colgroup': Colgroup,
           'col': Col,
           'tr': TR,
           'th': TH,
           'td': TD,
           'ins': Ins,
           'del': Del,
           'article': Article,
           'aside': Aside,
           'bdi': Bdi,
           'details': Details,
           'dialog': Dialog,
           'summary': Summary,
           'figure': Figure,
           'figcaption': Figcaption,
           'footer': Footer,
           'header': Header,
           'hgroup': Hgroup,
           'mark': Mark,
           'meter': Meter,
           'nav': Nav,
           'progress': Progress,
           'ruby': Ruby,
           'rt': Rt,
           'rp': Rp,
           'section': Section,
           'time': Time,
           'wbr': Wbr
           }

def elements_from_soup(soup,
                       remove_tags=['script','style','font','map','center'],
                       keep_attributes=['class','id','dir','lang','src','href','name','alt']):

    """Returns a tree of ferenda.html elements from a BeautifulSoup
    tree, removing some tags and attributes in the process."""
    if soup.name in remove_tags:
        return None
    if soup.name not in _tagmap:
        # self.log.warning("Can't render %s" % soup.name)
        # FIXME: Get ahold of the log object somehow
        print("WARNING: Can't render %s" % soup.name)
        return None
    attrs = {}
    for attr in keep_attributes:
        if attr in soup.attrs:
            # print("   %s has attr %s" % (soup.name,attr))
            if isinstance(soup[attr],list):
                attrs[attr] = " ".join(soup[attr])
            else:
                attrs[attr] = soup[attr]
    # print("%s: %r" % (soup.name, attrs))
                
    element = _tagmap[soup.name](**attrs)
    
    #print("%sNode: %s" % ((depth-1)*". ",soup.name))
    for child in soup.children:
        if isinstance(child,bs4.element.Comment):
            #print("%sChild comment" % (depth*". "))
            pass
        elif isinstance(child,bs4.NavigableString):
            #print("%sChild string %r" % (depth*". ",child[:10]))
            if six.text_type(child).strip() != "": # ignore pure whitespace between tags
                element.append(six.text_type(child)) # convert NavigableString to pure str
        else:
            #print("%sChild %s" % (depth*". ",soup.name))
            subelement = elements_from_soup(child,remove_tags,keep_attributes)
            if subelement != None:
                element.append(subelement)
    return element

